package cn.k7g.codeflow.model.node.impl;

import cn.k7g.codeflow.annotation.InParam;
import cn.k7g.codeflow.annotation.NodeFlag;
import cn.k7g.codeflow.invoke.CodeFlow;
import cn.k7g.codeflow.model.node.BaseCodeNode;
import cn.k7g.codeflow.model.node.CodeNode;
import cn.k7g.codeflow.model.node.InPortCodeNode;
import cn.k7g.codeflow.model.node.OutPortCodeNode;
import cn.k7g.codeflow.model.node.port.InCodePort;
import cn.k7g.codeflow.model.node.port.OutCodePort;
import cn.k7g.codeflow.utils.Util;

import java.lang.reflect.Method;
import java.lang.reflect.Parameter;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Optional;
import java.util.stream.Collectors;

/**
 * 函数节点
 */
public class MethodNode extends BaseCodeNode implements InPortCodeNode, OutPortCodeNode {

    private Method method;
    private NodeFlag nodeCfg;

    public MethodNode(Method method) {
        this.method = method;
        this.nodeCfg = method.getAnnotation(NodeFlag.class);
        assert nodeCfg != null: "nodeCfg is null";
        this.setLabel(this.nodeCfg.label());
    }

    @Override
    public String getId() {
        return Util.getMethodId(method);
    }

    @Override
    public List<InCodePort> getInPorts() {
        List<InCodePort> ret = new ArrayList<>();
        Parameter[] parameters = method.getParameters();
        for (int i = 0; i < parameters.length; i++) {
            Parameter parameter = parameters[i];
            if (parameter.getType() == CodeFlow.class) {
                continue;
            }
            Optional<InParam> inParam = Optional.ofNullable(parameter.getAnnotation(InParam.class));
            InCodePort item = new InCodePort();
            item.setIndex(i);
            item.setType(parameter.getType().getName());
            inParam.ifPresent(param -> item.setRemark(param.label()));
            ret.add(item);
        }
        return ret;
    }

    @Override
    public List<OutCodePort> getOutPorts() {
        return Arrays.stream(nodeCfg.outPorts()).map(o -> {
            OutCodePort outCodePort = new OutCodePort();
            outCodePort.setName(o.name());
            outCodePort.setType(o.type().getName());
            outCodePort.setRemark(o.remark());
            return outCodePort;
        }).collect(Collectors.toList());
    }
}
